#ifndef ENGINE_RESOURCE_GRAPHIC_H
#define ENGINE_RESOURCE_GRAPHIC_H


#include "../Renderer.h"
#include <list>
#include "SDL.h"
#include <string>
#include "../Synaptic/Misc.h"

namespace Engine { namespace Synaptic { class Surface; }  } 

namespace Engine {

namespace Resource {

typedef std::list<const Engine::Resource::Graphic *> GraphicList;
class Graphic {
  private:
	SDL_Surface * surface; 
	double rotation; // Degree of rotation TODO: how stepping would work with a rotated surface?
	std::string surfaceName; 
	Sint16 yScreenOffset; 
	Sint16 xScreenOffset; 
	Sint16 xFrameOffset; // 
	Sint16 yFrameOffset; // 
	Sint16 zOrder; 
	Misc::Frame frame; 
	Misc::Frame position; 

  public:
	static Graphic merge(GraphicList gfxList, std::string resultName); 
	static Graphic merge(GraphicList gfxList, unsigned int resultWidth, unsigned int resultHeight, std::string resultName); // Merge a list of graphics into a single graphic
	Graphic(std::string name, std::string package = "", Sint16 zOrder = 0); 
	Graphic(const Graphic & source, Sint16 zOrder = 0); 
	Graphic(SDL_Surface * surface, std::string surfaceName, Sint16 zOrder = 0); 
	virtual ~Graphic(); 
	inline Graphic & setFrameSize(int width, int height); 
	inline Graphic & setFrame(int row, int column); 
	inline Graphic & setFrameOffset(Sint16 x, Sint16 y); 
	inline Graphic & setPosition(int offX, int offY); // Set graphic position in relation to screen
	inline Graphic & setCenter(int centerX, int centerY); // Set center position in relation to screen
	inline Graphic & setPadding(int offX, int offY); // Set padding in relation to graphic position
	inline Graphic & setRotation(double degree = 0.0f); 
	inline void step(Misc::Direction direction = Misc::DIRECTION_RIGHT, bool wrap = false); 
	inline Graphic & rotate(double angle); 
	Graphic slice(int row, int column) const; 
	Graphic slice(const Misc::Frame & sliceFrame) const; 
	Graphic resize(double xFactor = 1.0, double yFactor = 1.0) const; // Resize the graphic to a given factor, including its frame size.
	Graphic resize(int xSize, int ySize) const; // Resize the graphic to a given size, including its frame size.
	inline bool operator<(const Graphic & against) const; 
  friend class Engine::Renderer;
};
inline Graphic & Graphic::setFrameSize(int width, int height) {
  	frame.w = width;
	frame.h = height;
	return *this;
}

inline Graphic & Graphic::setFrame(int row, int column) {
  	frame.x = column*frame.w;
	frame.y = row*frame.h;
	return *this;
}

inline Graphic & Graphic::setFrameOffset(Sint16 x, Sint16 y) {
  	xFrameOffset = x;
	yFrameOffset = y;
	return *this;
}

// Set graphic position in relation to screen
inline Graphic & Graphic::setPosition(int offX, int offY) {
	position.x = offX;
	position.y = offY;
	return *this;
}

// Set center position in relation to screen
inline Graphic & Graphic::setCenter(int centerX, int centerY) {
	position.x = centerX - frame.w/2;
	position.y = centerY - frame.h/2;
	return *this;
}

// Set padding in relation to graphic position
inline Graphic & Graphic::setPadding(int offX, int offY) {
  	xScreenOffset = offX;
	yScreenOffset = offY;
	return *this;
}

inline Graphic & Graphic::setRotation(double degree) {
	rotation = degree;
	return *this;
}

inline void Graphic::step(Misc::Direction direction, bool wrap) {

	// TODO: stepping with rotation algorithm
	switch (direction) {
		case (Misc::DIRECTION_UP):
			frame.y -= frame.h;
			break;
		case (Misc::DIRECTION_DOWN):
			frame.y += frame.h;
			break;
		case (Misc::DIRECTION_LEFT):
			frame.x -= frame.w;
			break;
		case (Misc::DIRECTION_RIGHT):
			frame.x += frame.w;
			break;
	}
	if (frame.x + frame.w > surface->w) {
		if (wrap) frame.x = 0;
		else frame.x = surface->w - frame.w;
	}
	if (frame.y + frame.h > surface->h) {
		if (wrap) frame.y = 0;
		else frame.y = surface->h - frame.h;
	}

	if (frame.x < 0) frame.x = 0;
	if (frame.y < 0) frame.y = 0;
}

inline Graphic & Graphic::rotate(double angle) {
	// rotation is performed by the renderization engine,
	// because it is necessary to recalculate x and y positions
	// to match the center.
  	rotation += angle;
	//if (rotation > 360.0) rotation -= 360.0;
	//if (rotation < 0.0f) rotation += 360.0;
	return *this;
}

inline bool Graphic::operator<(const Graphic & against) const {
	return (this->zOrder < against.zOrder);
}


} // namespace Engine::Resource

} // namespace Engine
#endif
